using System;
using System.Collections.Generic;
using System.Text;
using System.Text.RegularExpressions;
using Petra.Gis.Geometry;
using Petra.Gis.Geometry.Support;

namespace Petra.Gis.DataServices.WKT
{
  public partial class GeometryFactory : IGeometryFactory
  {

    //private pointers to functions to store in geometry mapping array ;-)
    //private delegate GeometryBase CreateGeometryFn(ref string inputStr, System.Type memberOf);
    private delegate GeometryBase CreateGeometryFn(RegexGeometry rGeom, System.Type memberOf);
    private delegate string SaveGeometryFn(GeometryBase geometry);

    //geomeytry mapping arrays
    //internal static List<WKTGeometryType> getWktName; //implemented geom types
    private static Dictionary<WKTGeometryType, CreateGeometryFn> storageToGeometry;
    private static Dictionary<System.Type, SaveGeometryFn> geometryToStorage;

    private static RegexGeometryParser rgp;
    private static RegexGeometry rg;
    


    static GeometryFactory()
    {

      //getWktName = RegexGeometryParser.ImplementedTypes;

      //setup storage to geometry functions
      storageToGeometry = new Dictionary<WKTGeometryType, CreateGeometryFn>();
      storageToGeometry.Add(WKTGeometryType.Point, new CreateGeometryFn(ToPoint));
      storageToGeometry.Add(WKTGeometryType.LineString, new CreateGeometryFn(ToLineString));
      storageToGeometry.Add(WKTGeometryType.Polygon, new CreateGeometryFn(ToPolygon));
      storageToGeometry.Add(WKTGeometryType.GeometryCollection, new CreateGeometryFn(ToGeometryCollection));
      storageToGeometry.Add(WKTGeometryType.MultiPoint, new CreateGeometryFn(ToMultiPoint));
      storageToGeometry.Add(WKTGeometryType.MultiLineString, new CreateGeometryFn(ToMultiLine));
      storageToGeometry.Add(WKTGeometryType.MultiPolygon, new CreateGeometryFn(ToMultiPolygon));

      //setup geometry to storage functions
      geometryToStorage = new Dictionary<System.Type,SaveGeometryFn>();
      geometryToStorage.Add(typeof(Point), new SaveGeometryFn(PointToString));
      //geometryToStorage.Add(typeof(Label), new SaveGeometryFn(labelToString));
      geometryToStorage.Add(typeof(LineString), new SaveGeometryFn(LineStringToString));
      geometryToStorage.Add(typeof(Polygon), new SaveGeometryFn(PolygonToString));
      //geometryToStorage.Add(typeof(Arc), new SaveGeometryFn(arcToString));
      //geometryToStorage.Add(typeof(LineRing), new SaveGeometryFn(LineRingToString));

      geometryToStorage.Add(typeof(GeometryCollection), new SaveGeometryFn(GeometryCollectionToString));
      geometryToStorage.Add(typeof(MultiPoint), new SaveGeometryFn(MultiPointToString));
      //geometryToStorage.Add(typeof(MultiLabel), new SaveGeometryFn(multiLineToString));
      geometryToStorage.Add(typeof(MultiLine), new SaveGeometryFn(MultiLineToString));
      geometryToStorage.Add(typeof(MultiArea), new SaveGeometryFn(MultiPolygonToString));

      //geometryToStorage.Add(typeof(Path), new SaveGeometryFn(pathToString));
      //geometryToStorage.Add(typeof(PathRing), new SaveGeometryFn(pathRingToString));

    }


    //-----------------------begin interface functions block--------------------------


    public GeometryBase CreateGeometry(object vData)
    {
      string g = (string)vData;

      return _createGeometry(ref g, null, RegexGeometryParser.PreferedRegexTypeEnum.PreciseValidation);
    }



    //interface function saveGeometry
    public object SaveGeometry(GeometryBase oGeom)
    { 
      return (object)_saveGeometryBlob(oGeom);
    }



    //interface function canSave
    public bool CanSave(GeometryBase geometry)
    {
      //search Dictionary for delegate for this geometry
      try
      {
        SaveGeometryFn d = geometryToStorage[geometry.GetType()];
      }
      catch {
        return false;
      }
      return true;
    }
    //-----------------------end interface functions block--------------------------


    //public GeometryBase CreateGeometry(ref object vData)
    //{
    //  string g = (string)vData;

    //  return _createGeometry(ref g, null);
    //}


    /// <summary>
    /// additional function to interface functions (WKT specific), where input
    /// string is sent directly to 
    /// </summary>
    /// <param name="vData">reference to string with WKT data</param>
    /// <returns>BaseGeometry of proper type or throws exception when input string is invalid</returns>
    public GeometryBase CreateGeometry(ref string vData)
    {
      return _createGeometry(ref vData, null, RegexGeometryParser.PreferedRegexTypeEnum.PreciseValidation);
    }




    /// <summary>
    /// additional function to interface functions (WKT specific), where input is in
    /// strict format (no extra spaces, \n \t,.. chars...)
    /// </summary>
    /// <param name="vData">reference to string with WKT data</param>
    /// <returns>BaseGeometry of proper type or throws exception when input string is invalid</returns>
    public GeometryBase CreateGeometryBulk(ref string vData)
    {
      //RegexGeometryParser.PreferedTextParser = RegexGeometryParser.PreferedRegexTypeEnum.HighPerformance;
      return _createGeometry(ref vData, null, RegexGeometryParser.PreferedRegexTypeEnum.HighPerformance);
    }




    /// <summary>
    /// internal implementation of createGeometry
    /// </summary>
    /// <param name="inputStr">WKT formatted string data</param>
    /// <param name="parentGeomType">resolves geometries with hierarchy such as polygon and multi</param>
    /// <returns></returns>
    private static GeometryBase _createGeometry(ref string inputStr, System.Type parentGeomType, RegexGeometryParser.PreferedRegexTypeEnum p)
    {
      WKTGeometryType gType;

      gType = RegexGeometryParser.GetGeometryType(ref inputStr);

      //create RegexGeometry which is universal object graph
      //for any Petra.Gis geometry type
      rgp = new RegexGeometryParser(gType, p);
      rg = rgp.ParseGeometry(ref inputStr);

      //call function in dictionary that will create geometry from byte[]
      //return storageToGeometry[gType](ref inputStr, parentGeomType);
      return storageToGeometry[gType](rg, parentGeomType);

    }



    private static string _saveGeometryBlob(GeometryBase geometry)
    {
      return geometryToStorage[geometry.GetType()](geometry);
    }


    
  }

}
